package org.fastsyncer.manager.framework;

import java.io.File;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.fastsyncer.common.constant.MappingConstant;
import org.fastsyncer.common.entity.MappingTask;
import org.fastsyncer.manager.util.FileUtil;
import org.fastsyncer.manager.util.SerializableUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 主要用来存储MappingTask配置信息和执行MappingTask错误日志
 * 
 * @author AE86
 * @date 2017年5月17日 下午9:48:59
 * @version 1.0.0
 */
public final class DataFactory extends AbstractDirectory {

    private static final Logger logger = LoggerFactory.getLogger(DataFactory.class);

    private static volatile DataFactory instance = null;

    private DataFactory() {
    }

    public static DataFactory getInstance() {
        if (null == instance) {
            synchronized (DataFactory.class) {
                if (null == instance) {
                    instance = new DataFactory();
                }
            }
        }
        return instance;
    }

    private static final Map<String, Object> data = new ConcurrentHashMap<String, Object>();
    
    private static final Map<String, Object> meta = new ConcurrentHashMap<String, Object>();

    public void start() {
        // 加载历史Mapping数据
        File[] list = new File(storePath).listFiles();
        logger.info("Loading driver configs > " + storePath);
        if (null != list) {
            String p = null;
            File map = null;
            File m = null;
            String id;
            for (File n : list) {
                try {
                    id = n.getName();
                    p = new StringBuilder().append(n.getAbsolutePath()).append(File.separatorChar).append(id).toString();
                    map = new File(p + MappingConstant.STORE_MAPPING_SUFFIX);
                    if (!map.exists()) {
                        continue;
                    }
                    // 读取驱动配置
                    data.put(id, SerializableUtil.deSerializable(map));
                    
                    // 读取增量配置
                    m = new File(p + MappingConstant.STORE_META_SUFFIX);
                    if (m.exists()) {
                        meta.put(id, SerializableUtil.deSerializable(m));
                    }
                } catch (Exception e) {
                    logger.error("Error when loading history mapping configuration:" + e.getCause(), e.getClass());
                }
            }
        }
    }

    /**
     * 获取MappingTask配置
     * 
     * @param key
     *            MappingTask名称
     * @return MappingTask
     */
    public MappingTask getMapping(String key) {
        return (MappingTask) data.get(key);
    }

    /**
     * 获取所有的MappingTask配置
     * 
     * @param key
     *            MappingTask名称
     * @return MappingTask
     */
    public Map<String, Object> getMappings() {
        // 禁止直接修改该集合,当修改该集合时会抛出异常UnsupportedOperationException
        return Collections.unmodifiableMap(data);
    }

    // MappingTask配置关系
    public boolean saveMapping(String key, Object value) {
        try {
            synchronized (data) {
                StringBuilder mapPath = new StringBuilder();
                // 例如D:\fastsyncer\8ac99237e022477b9aeaa84da421c1e2
                mapPath.append(storePath).append(File.separatorChar).append(key);
                // 检查文件夹
                FileUtil.createDirIfNotExist(mapPath.toString());

                // 例如D:\fastsyncer\8ac99237e022477b9aeaa84da421c1e2\8ac99237e022477b9aeaa84da421c1e2.config
                mapPath.append(File.separatorChar).append(key).append(MappingConstant.STORE_MAPPING_SUFFIX);
                // 序列化到磁盘
                SerializableUtil.serializable(new File(mapPath.toString()), value);
                data.put(key, value);
                return true;
            }
        } catch (Exception e) {
            logger.error("Storage failed:" + e.getCause(), e.getClass());
        }
        return false;
    }
    
    // 删除MappingTask
    public boolean removeMapping(String key) {
        meta.remove(key);
        Object remove = data.remove(key);
        if (remove != null) {
            // 例如D:\fastsyncer\8ac99237e022477b9aeaa84da421c1e2
            String mapPath = new StringBuilder().append(storePath).append(File.separatorChar).append(key).toString();
            FileUtil.deleteDir(new File(mapPath));
            return true;
        }
        return false;
    }
    
    //*************** meta *******************//    
    @SuppressWarnings("unchecked")
    public <T> T getMappingMeta(String key, Class<T> clazz) {
        return (T) meta.get(key);
    }

    public <T> boolean saveMappingMeta(String key, T value) {
        try {
            synchronized (meta) {
                StringBuilder mapPath = new StringBuilder();
                // 例如D:\fastsyncer\8ac99237e022477b9aeaa84da421c1e2
                mapPath.append(storePath).append(File.separatorChar).append(key);
                // 检查文件夹
                FileUtil.createDirIfNotExist(mapPath.toString());

                // 例如D:\fastsyncer\8ac99237e022477b9aeaa84da421c1e2\8ac99237e022477b9aeaa84da421c1e2.meta
                mapPath.append(File.separatorChar).append(key).append(MappingConstant.STORE_META_SUFFIX);
                // 序列化到磁盘
                SerializableUtil.serializable(new File(mapPath.toString()), value);
                meta.put(key, value);
                return true;
            }
        } catch (Exception e) {
            logger.error("Storage failed:" + e.getCause(), e.getClass());
        }
        return false;
    }

    public boolean removeMappingMeta(String key) {
        meta.remove(key);
        // 例如D:\fastsyncer\8ac99237e022477b9aeaa84da421c1e2\8ac99237e022477b9aeaa84da421c1e2.meta
        String mapPath = new StringBuilder().append(storePath).append(File.separatorChar).append(key).append(File.separatorChar).append(key).append(MappingConstant.STORE_META_SUFFIX).toString();
        FileUtil.deleteDir(new File(mapPath));
        return true;
    }
    
}
